package git

import (
	"context"
	"gitee.com/kmyss/gf-ex/yscmd"
	"gitee.com/kmyss/gf-ex/yslog"
	"gitee.com/kmyss/gf-ex/ysos"
	"gitee.com/kmyss/ys-cli/internal/model"
	"gitee.com/kmyss/ys-cli/internal/service"
	"github.com/gogf/gf/v2/container/gmap"
	"github.com/gogf/gf/v2/container/gset"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/text/gregex"
)

const (
	commitPattern = `^(\w*)(?:\(([\w\$\.\-\*\s]*)\))?\:\s(.+)$`
	mergePattern  = `^[Mm]erge`
)

var types = []string{
	"build",
	"ci",
	"docs",
	"feat",
	"fix",
	"perf",
	"refactor",
	"style",
	"test",
	"chore",
	"version",
}

func init() {
	service.RegisterGit(&sGit{})
}

type sGit struct {
	l *glog.Logger // 日志

	instance *gmap.StrAnyMap
}

func (s *sGit) log() *glog.Logger {
	if s.l == nil {
		s.l = yslog.Log("logic.git")
	}
	return s.l
}

func (s *sGit) ins() *gmap.StrAnyMap {
	if s.instance == nil {
		s.instance = gmap.NewStrAnyMap()
	}
	return s.instance
}

func (s *sGit) CheckCommitMsg(ctx context.Context, in string) (err error) {

	if !g.IsEmpty(in) && !gfile.Exists(in) {
		return gerror.Newf("Commit 文件不存在! %s", in)
	}

	var lines []string

	_ = gfile.ReadLines(in, func(text string) error {
		lines = append(lines, text)
		return nil
	})

	if len(lines) == 0 {
		return gerror.Newf("提交信息为空! %s", in)
	}

	if gregex.IsMatchString(mergePattern, lines[0]) {
		return
	}

	if gregex.IsMatchString(commitPattern, lines[0]) {
		match, _ := gregex.MatchString(commitPattern, lines[0])
		if s.types(ctx).Contains(match[1]) {
			return
		}
	}

	return gerror.New("前缀错误")
}

func (s *sGit) CommitNoteStr(ctx context.Context) string {
	return `
    语义化提交格式如下:
        <type>(optional scope): <description>
    
        <类型>(可选 范围): <描述>
   
    此工具只检查第一行提交信息, 其中 <类型> 和 <描述> 必须存在.
    <类型> 可选的名称如下:
        build: 影响构建系统或外部依赖项的更改（示例范围：gulp、brocoli、npm）
        ci: 对 CI 配置文件和脚本的更改（示例范围：Travis、Circle、BrowserStack、SauceLabs）
        docs: 只修改的文档
        feat: 一个新的功能
        fix: 修复一个 bug
        perf: 提高代码性能的修改
        refactor: 重构代码不引入新的bug或功能的
        style: 不影响代码含义的更改（空白、格式、缺少分号等）
        test: 添加缺失的测试或更正现有测试
        chore: 日常维护更新
        version: 版本发布位置, 此提交是与版本相关内容包括 changelog 版本version修改 等 并在此位置打版本标签
`
}

func (s *sGit) BundleCreate(ctx context.Context, in *model.BundleConfigInput) (err error) {
	if g.IsEmpty(in.Branch) {
		in.Branch, err = s.branchSelect(ctx)
		if err != nil {
			return
		}
	}

	in.Show()

	err = s.createBundle(ctx, in)
	if err != nil {
		return
	}

	ysos.OpenDir(gfile.Dir(in.Path))

	return
}

func (s *sGit) BundleClone(ctx context.Context, in *model.BundleConfigInput) (err error) {
	err = s.cloneBundle(ctx, in)
	if err != nil {
		return err
	}

	err = s.createNewBranchAndCheckOut(ctx, in)
	if err != nil {
		return err
	}

	return
}

func (s *sGit) BundlePull(ctx context.Context, in *model.BundleConfigInput) (err error) {
	err = s.checkOut(ctx, in)
	if err != nil {
		return
	}

	err = s.setOrigin(ctx, in)
	if err != nil {
		return
	}

	err = s.pull(ctx, in)
	if err != nil {
		return
	}

	err = s.createNewBranchAndCheckOut(ctx, in)
	if err != nil {
		return
	}

	return
}

func (s *sGit) BundleMerge(ctx context.Context, in *model.BundleConfigInput) (err error) {

	err = s.setOrigin(ctx, in)
	if err != nil {
		return
	}

	err = s.RemotePrune(ctx, in)
	if err != nil {
		return
	}

	err = s.remoteUpdate(ctx)
	if err != nil {
		return
	}

	err = s.merge(ctx, in)
	if err != nil {
		return
	}

	return
}

func (s *sGit) CanPull(ctx context.Context) (out bool, err error) {
	status, err := s.status(ctx)
	if err != nil {
		return false, err
	}

	if len(status["changed"]) > 0 || len(status["commited"]) > 0 {
		yslog.Print("有未提交的文件，不能切换分支")
		return false, nil
	} else if len(status["unstaged"]) > 0 {
		scan := yscmd.ScanWithDefaultN("有未跟踪文件，确认要拉取吗？[y/n]", "n")
		if scan != "y" {
			return false, nil
		}
	}

	return true, nil

}

func (s *sGit) types(ctx context.Context) (out *gset.StrSet) {
	out = gset.NewStrSet()

	for _, t := range types {
		out.AddIfNotExist(t)
	}

	return
}
